/*
 *  Model.java 
 *    
 *    Copyright 2012 YAWP Project Team
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.googlecode.yawp.model;

import java.util.GregorianCalendar;
import java.util.Calendar;
import java.util.List;
import java.util.Observable;

/**
 * This class is an abstract representation of the YAWP data model.  Controller
 * and view classes should make all requests of state through this model.
 * 
 * @author Dan Spuches
 * @author Grace Hopkins
 * @author Craig Cato
 *
 */
public abstract class Model extends Observable {
	
	/**
	 * Determine if a user exists with the specified name.  Returns true if the
	 * user already exists, false if not.
	 * 
	 * @param name The user name to check for
	 * @return True if user exists, false if not
	 */
	public abstract boolean isExistingUser(String name);
	
	/**
	 * Determine if a data entry already exists for the specified date
	 * 
	 * @param date The date to check for
	 * @return True if the date has a data entry, false if not
	 */
	public abstract boolean hasExistingWeightEntry(Calendar date);
	
	
	/**
	 * Create a new user with the specified name and preferences.  
	 * 
	 * @param name User name
	 * @param preferences Preferences for this user
	 * @throws Exception An exception is thrown if the user cannot be created
	 */
	public abstract void createUser(String name, Preferences preferences) throws Exception;
	
	/**
	 * Sets the current user to the specified name.  This will load the 
	 * state of that user from persistent storage.  
	 * 
	 * @param name The user to select and load data on
	 */
	public abstract void setCurrentUser(String name) throws Exception;
	
	
	/**
	 * This method determines if there is currently an active user for the application.
	 * An active user is defined as one who's data has been loaded into the application
	 * or a new user
	 * 
	 * @return True if there is an active user, false if not
	 */
	public abstract boolean hasActiveUser();
	
	/**
	 * Sets a flag that determines if there is currently an active user for the application.
	 * An active user is defined as one who's data has been loaded into the application
	 * or a new user
	 * 
	 * @param value True if there is an active user, false if not
	 */
	protected abstract void setHasActiveUser(boolean value);
	
	/**
	 * Saves all user data to persistent storage
	 */
	public abstract void saveUserData();
	
	/**
	 * This method is called when the application is exiting to allow for the model
	 * classes and persistent storage to be cleaned up gracefully.
	 */
	public abstract void shutdown();
	
	/**
	 * Return the preferences for the current user
	 * 
	 * @return Preferences for the current user
	 */
	public abstract Preferences getPreferences();
	
	/**
	 * Get the current user's name
	 * 
	 * @return Current user's name
	 */
	public abstract String getUserName();
	
	/**
	 * Get the current user's units
	 * 
	 * @return Current user's units
	 */
	public abstract Preferences.Units getUserUnits();
	
	/**
	 * get the current users data view setting
	 * @return current users data view setting
	 */
	public abstract Preferences.DataView getUserDataView();
	
	/**
	 * Get the current user's target weight
	 * 
	 * @return Current user's target weight
	 */
	public abstract double getUserTargetWeight();
	
	/**
	 * Get the current user's height
	 * 
	 * @return Current user's height
	 */
	public abstract double getUserHeight();
	
	/**
	 * Set's current user's name
	 * 
	 * @param userName User's name
	 */
	public abstract void setUserName(String userName);
	
	/**
	 * Set the current user's units
	 * 
	 * @param units User's units
	 */
	public abstract void setUserUnits(Preferences.Units units);
	
	/**
	 * Set the current user's target weight
	 * 
	 * @param weight User's weight
	 */
	public abstract void setUserTargetWeight(double weight);
	
	/**
	 * Set the current user's height in the specified units
	 * 
	 * @param height User's height
	 */
	public abstract void setUserHeight(double height);
	
	
	/**
	 * Set the current user's preferences
	 * 
	 * @param prefs The preferences object to set
	 */
	public abstract void setPreferences(Preferences prefs);
	
	/**
	 * Get the specified day's weights
	 * 
	 * @param date - date to get weight for
	 * @return Weight Entry for the specified date
	 */
	public abstract double getDayWeight(Calendar date);
	
	
	/**
	 * Get the specified month's weights
	 * 
	 * @param month Month to get data on
	 * @return List of weight entries for the specified month
	 */
	public abstract List<WeightEntry> getMonthWeights(int month, int year);
	
	/**
	 * Get the specified year's weights
	 * 
	 * @param year Year to get data on
	 * @return List of weight entries for the specified year
	 */
	public abstract List<WeightEntry> getYearWeights(int year);
	
	/**
	 * Get the current month
	 * 
	 * @return Current month
	 */
	public static int getCurrentMonth() {
		Calendar today = new GregorianCalendar();
		
		int calMonth = today.get(Calendar.MONTH);
		int month = -1;
		
		switch (calMonth) {
		case Calendar.JANUARY:
			month = 1;
			break;
		case Calendar.FEBRUARY:
			month = 2;
			break;
		case Calendar.MONTH:
			month = 3;
			break;
		case Calendar.APRIL:
			month = 4;
			break;
		case Calendar.MAY:
			month = 5;
			break;
		case Calendar.JUNE:
			month = 6;
			break;
		case Calendar.JULY:
			month = 7;
			break;
		case Calendar.AUGUST:
			calMonth = 8;
			break;
		case Calendar.SEPTEMBER:
			month = 9;
			break;
		case Calendar.OCTOBER:
			month = 10;
			break;
		case Calendar.NOVEMBER:
			month = 11;
			break;
		case Calendar.DECEMBER:
			month = 12;
			break;
		}
		
		return month;
	}
	
	/**
	 * Get the current year
	 * 
	 * @return Current year
	 */
	public static int getCurrentYear() {
		Calendar today = new GregorianCalendar();
		
		return today.get(Calendar.YEAR);
	}
	
	/**
	 * Get number of days in the specified month
	 * 
	 * @param month The month to check where 1 = January...12 = December
	 * @param year The year to check for.  This is important for leap years.
	 * @return Number of days in specified month or -1 if there was erroneous
	 * data in the parameters
	 */
	public static int getDaysOfMonth(int month, int year) {
		int calMonth;
		
		switch (month) {
		case 1:
			calMonth = Calendar.JANUARY;
			break;
		case 2:
			calMonth = Calendar.FEBRUARY;
			break;
		case 3:
			calMonth = Calendar.MONTH;
			break;
		case 4:
			calMonth = Calendar.APRIL;
			break;
		case 5:
			calMonth = Calendar.MAY;
			break;
		case 6:
			calMonth = Calendar.JUNE;
			break;
		case 7:
			calMonth = Calendar.JULY;
			break;
		case 8:
			calMonth = Calendar.AUGUST;
			break;
		case 9:
			calMonth = Calendar.SEPTEMBER;
			break;
		case 10:
			calMonth = Calendar.OCTOBER;
			break;
		case 11:
			calMonth = Calendar.NOVEMBER;
			break;
		case 12:
			calMonth = Calendar.DECEMBER;
			break;
		default:
			return -1;
		}
		
		
		Calendar date = new GregorianCalendar(year, calMonth, 1);
		return date.getActualMaximum(Calendar.DAY_OF_MONTH);
	}
	
	
	/**
	 * Return a string that provides the user with a greeting and message
	 * of the day
	 * 
	 * @return Message of the day
	 */
	public abstract String getUserStatus();
	
	
	/**
	 * Adds a weight for the specified date
	 * 
	 * @param date Date of the entry
	 * @param weight Weight of this entry
	 * @throws Exception An exception is thrown if there were issues encountered while adding
	 * the weight entry.
	 */
	public abstract void addWeight(Calendar date, double weight) throws Exception;
	
	/**
	 * Deletes the weight for the specified date
	 * 
	 * @param date Date of the entry
	 * @throws Exception An exception is thrown if there were issues encountered while deleting
	 * the weight entry.
	 */
	public abstract void deleteWeight(Calendar date) throws Exception;

	/** 
	 * Get the users preference to auto fill in gaps in weights with weight proceeding gap
	 * 
	 * @return true if auto fill is set
	 */
	public abstract boolean getUserAutoFill();
	
	/** 
	 * Get the earliest date with a weight.
	 * 
	 * @return Calendar
	 */
	public abstract Calendar getEarliestDate();
	
	/** 
	 * clearAllData()
	 * !!! DANGER !!!! This deletes all data in the database and is intended
	 *    to be for testing.
	 */
	public abstract void clearAllData() throws Exception;




}
